package com.minidao.framework.aop;

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;

import com.minidao.framework.annotation.MiniSql;
import com.minidao.framework.annotation.ResultType;
import com.minidao.framework.dao.IMiniSupportDao;
import com.minidao.framework.rowMapper.MapRowMapper;
import com.minidao.framework.util.MiniDaoUtil;

public class MiniDaoHandler implements MethodInterceptor {

	private IMiniSupportDao miniSupportDao;
	private JdbcTemplate jdbcTemplate;

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Method method = invocation.getMethod();
		Object[] args = invocation.getArguments();
		String templateSql = null;

		if (!MiniDaoUtil.isAbstract(method)) {
			return invocation.proceed();
		}
		Map<String, Object> rs = new HashMap<String, Object>();
		if (miniDaoHiber(rs, method, args)) {
			return rs.get("returnObj");
		}

		templateSql = this.installDaoMetaData(method);
		templateSql = this.parseSqlTemplate(method, templateSql);

		Class<?> returnType = method.getReturnType();
		if (returnType.isAssignableFrom(List.class)) {
			ResultType resultType = method.getAnnotation(ResultType.class);
			String value = null;
			if (resultType != null) {
				value = resultType.value();
			}
			if (value == null || "java.util.Map".equals(value)) {
				return jdbcTemplate.query(templateSql, args, new MapRowMapper());
			} else {
				Class<?> clazz = null;
				try {
					clazz = Class.forName(resultType.value());
				} catch (Exception e) {
					e.printStackTrace();
				}
				return jdbcTemplate.query(templateSql, args, ParameterizedBeanPropertyRowMapper.newInstance(clazz));
			}
		} else {
			return jdbcTemplate.queryForObject(templateSql, args, ParameterizedBeanPropertyRowMapper.newInstance(returnType));
		}
	}

	private boolean miniDaoHiber(Map<String, Object> rs, Method method, Object[] args) {
		if (method.getName().equals("save")) {
			this.miniSupportDao.save(args[0]);
			return true;
		}
		if (method.getName().equals("update")) {
			this.miniSupportDao.update(args[0]);
			return true;
		}
		if (method.getName().equals("delete")) {
			this.miniSupportDao.delete(args[0]);
			return true;
		}
		if (method.getName().equals("getById")) {
			Class<?> clz = (Class<?>) args[0];
			rs.put("returnObj", this.miniSupportDao.getById(clz, Integer.parseInt(args[1].toString())));
			return true;
		}
		return false;
	}

	private String installDaoMetaData(Method method) {
		String templateSql = null;

		// 如果有MiniSql执行MiniSql
		if (method.isAnnotationPresent(MiniSql.class)) {
			MiniSql sql = (MiniSql) method.getAnnotation(MiniSql.class);
			if (StringUtils.isNotEmpty(sql.sql())) {
				templateSql = sql.sql();
			}
		}
		return templateSql;
	}

	@SuppressWarnings("unchecked")
	private String parseSqlTemplate(Method method, String templateSql) {
		String executeSql = null;

		if (StringUtils.isNotEmpty(templateSql)) {
			executeSql = templateSql;
		} else {
			String sqlTempletPath = method.getDeclaringClass().getName().replace(".", "/").replace("/dao/", "/sql/") + ".xml";
			String path = getClass().getClassLoader().getResource(sqlTempletPath).getPath();
			try {
				Element root = this.getRoot(path);
				List<Element> es = root.elements();
				for (Element e : es) {
					if (e.attributeValue("id").equals(method.getName())) {
						executeSql = e.getText();
					}
				}
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
		return executeSql;
	}

	private Document getDocument(String path) throws Exception {
		SAXReader reader = new SAXReader();
		Document document = reader.read(new FileInputStream(path));
		return document;
	}

	private Element getRoot(String path) throws Exception {
		Document document = this.getDocument(path);
		Element root = document.getRootElement();
		return root;
	}

	public IMiniSupportDao getMiniSupportDao() {
		return miniSupportDao;
	}

	public void setMiniSupportDao(IMiniSupportDao miniSupportDao) {
		this.miniSupportDao = miniSupportDao;
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public static void main(String[] args) {
		try {
			Class<?> clazz1 = Class.forName("java.util.HashMap");
			Class<?> clazz2 = Class.forName("java.util.Map");
			System.out.println(clazz1);
			System.out.println(clazz2);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}
